/*********************************************************************************
 * Copyright (c) 2006 Forschungszentrum Juelich GmbH 
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * (1) Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer at the end. Redistributions in
 * binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other
 * materials provided with the distribution.
 * 
 * (2) Neither the name of Forschungszentrum Juelich GmbH nor the names of its 
 * contributors may be used to endorse or promote products derived from this 
 * software without specific prior written permission.
 * 
 * DISCLAIMER
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 ********************************************************************************/
package pl.umk.mat.fred.unicore.urc.storagemanagement.dialogs;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ISelectionStatusValidator;
import org.eclipse.ui.dialogs.SelectionStatusDialog;
import org.eclipse.ui.internal.WorkbenchMessages;

import pl.umk.mat.fred.unicore.urc.storagemanagement.StorageManagementActivator;
import pl.umk.mat.fred.unicore.urc.storagemanagement.actions.DoubleClickAction;
import pl.umk.mat.fred.unicore.urc.storagemanagement.actions.HideShowStoragesAction;
import pl.umk.mat.fred.unicore.urc.storagemanagement.tree.ContentProviderImplementation;
import pl.umk.mat.fred.unicore.urc.storagemanagement.tree.LabelProviderImplementation;
import pl.umk.mat.fred.unicore.urc.storagemanagement.tree.nodes.GridNode;
import pl.umk.mat.fred.unicore.urc.storagemanagement.tree.nodes.RootNode;
import pl.umk.mat.fred.unicore.urc.storagemanagement.tree.sorters.NameSorter;

import de.fzj.unicore.rcp.common.utils.ToolBarUtils;
import de.fzj.unicore.rcp.servicebrowser.ServiceBrowserActivator;
import de.fzj.unicore.rcp.servicebrowser.ServiceContentProvider;
import de.fzj.unicore.rcp.servicebrowser.ui.DefaultFilterSet;

/**
 * A panel in which nodes of certain types can be selected. The selected nodes
 * can be retrieved with the {@link #getResult()} method. Remember to ALWAYS
 * dispose of the nodes created by this dialog by calling
 * {@link #disposeNodes()} once you are done with looking at the result nodes.
 * 
 * @author demuth
 * 
 */
public class ServiceSelectionDialog extends SelectionStatusDialog implements
		PropertyChangeListener {

	protected ServiceContentProvider contentProvider;

	protected Set<String> interestingTypes;

	protected int expandToLevel = AbstractTreeViewer.ALL_LEVELS;
	
	private TreeViewer treeViewer;

	protected GridNode gridNode;

	private ISelectionStatusValidator fValidator = null;

	private String fEmptyListMessage = WorkbenchMessages.ElementTreeSelectionDialog_nothing_available;

	private IStatus fCurrStatus = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
			IStatus.OK, "", null); //$NON-NLS-1$

	private DefaultFilterSet defaultFilterSet;

	// toolbar manager used to add additional items
	// to the toolbar above the tree viewer
	private ToolBarManager upperToolBarManager = new ToolBarManager(SWT.RIGHT);

	private DoubleClickAction doubleClickAction;
	
	private Object fInput;

	private int fWidth = 60;

	private int fHeight = 18;
	
	private boolean allowMultiple = false;

	public ServiceSelectionDialog() {
		this(null);
	}

	/**
	 * Create a ServiceSelectionDialog.
	 * 
	 * @param interestingTypes
	 *            the types of nodes to be displayed
	 */
	public ServiceSelectionDialog(Set<String> interestingTypes) {
		this(interestingTypes, null);
	}

	/**
	 * Create a ServiceSelectionDialog.
	 * 
	 * @param interestingTypes
	 *            the types of nodes to be displayed
	 * @param selectedNodes
	 *            the default selection
	 */
	public ServiceSelectionDialog(Set<String> interestingTypes,
			List<IAdaptable> defaultSelectedNodes) {
		this(interestingTypes, defaultSelectedNodes, -1);
	}

	/**
	 * Create a ServiceSelectionDialog.
	 * 
	 * @param interestingTypes
	 *            the types of nodes to be displayed
	 * @param selectedNodes
	 *            the default selection
	 * @param maxDepth
	 *            maximal depth of services to be displayed. Used for limiting
	 *            memory and CPU usage.
	 */
	public ServiceSelectionDialog(Set<String> interestingTypes,
			List<IAdaptable> defaultSelectedNodes, int maxDepth) {
		super(ServiceBrowserActivator.getDefault().getWorkbench().getDisplay()
				.getActiveShell());

		setResult(new ArrayList(0));
		setStatusLineAboveButtons(true);

		int shellStyle = getShellStyle();
		setShellStyle(shellStyle | SWT.MAX | SWT.RESIZE);

		this.interestingTypes = interestingTypes;
		fInput = StorageManagementActivator.getDefault().getRootNode().clone();
	}

	/**
	 * Set the result using the super class implementation of setResult.
	 * 
	 * @param result
	 * @see SelectionStatusDialog#setResult(int, Object)
	 */
	protected void access$setResult(List result) {
		super.setResult(result);
	}

	/**
	 * Set the result using the super class implementation of buttonPressed.
	 * 
	 * @param id
	 * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int)
	 */
	protected void access$superButtonPressed(int id) {
		super.buttonPressed(id);
	}

	private void access$superCreate() {
		super.create();
	}

	/**
	 * Handles cancel button pressed event.
	 */
	@Override
	protected void cancelPressed() {
		setResult(null);
		super.cancelPressed();
	}

	@Override
	public boolean close() {
		boolean success = super.close();
		if (contentProvider != null) {
			contentProvider.removePropertyChangeListener(this);
		}
		if (defaultFilterSet != null) {
			defaultFilterSet.dispose();
		}
		return success;
	}

	/*
	 * @see SelectionStatusDialog#computeResult()
	 */
	@Override
	protected void computeResult() {
		setResult(((IStructuredSelection) treeViewer.getSelection()).toList());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.window.Window#create()
	 */
	@Override
	public void create() {
		BusyIndicator.showWhile(null, new Runnable() {
			public void run() {
				access$superCreate();
				// TODO
				//treeViewer.setSelection(new StructuredSelection(
				//		getInitialElementSelections()), true);
				updateOKStatus();
			}
		});
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jface.dialogs.Dialog#createButtonBar(org.eclipse.swt.widgets
	 * .Composite)
	 */
	@Override
	protected Control createButtonBar(Composite parent) {
		Font font = parent.getFont();
		Composite composite = new Composite(parent, SWT.NONE);
		GridLayout layout = new GridLayout();
		layout.marginWidth = 0;
		layout.marginHeight = 0;
		layout.horizontalSpacing = 0;
		composite.setLayout(layout);
		composite
				.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		composite.setFont(font);

		Control buttonSection = super.createButtonBar(composite);
		((GridData) buttonSection.getLayoutData()).grabExcessHorizontalSpace = true;

		return composite;
	}

	/*
	 * @see Dialog#createDialogArea(Composite)
	 */
	@Override
	protected Control createDialogArea(Composite parent) {
		Composite composite = (Composite) super.createDialogArea(parent);
		treeViewer = createTreeViewer(composite);

		Label messageLabel = createMessageArea(composite);

		int numColumns = 1;
		GridLayout layout = new GridLayout(numColumns, true);

		Composite upperToolBarContainer = new Composite(composite, SWT.NO_FOCUS);
		upperToolBarContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL,
				true, false));
		upperToolBarContainer.setLayout(layout);

		HideShowStoragesAction hideShowAction = new HideShowStoragesAction(treeViewer);
		upperToolBarManager.add(hideShowAction);

		ToolBar upperToolBar = upperToolBarManager
				.createControl(upperToolBarContainer);
		upperToolBar.setLayout(new GridLayout(upperToolBar.getItemCount(),
				false));
		GridData data = new GridData(SWT.RIGHT, SWT.CENTER, true, false);
		upperToolBar.setLayoutData(data);
		ToolBarUtils.setToolItemText(upperToolBar);

		data = new GridData(GridData.FILL_BOTH);
		data.widthHint = convertWidthInCharsToPixels(fWidth);
		data.heightHint = convertHeightInCharsToPixels(fHeight);

		Tree treeWidget = treeViewer.getTree();
		treeWidget.setLayoutData(data);
		treeWidget.setFont(parent.getFont());

		if (isTreeEmpty()) {
			messageLabel.setEnabled(false);
		}

		return composite;
	}

	/**
	 * Creates the tree viewer.
	 * 
	 * @param parent
	 *            the parent composite
	 * @return the tree viewer
	 */
	protected TreeViewer createTreeViewer(Composite parent) {
		
		final TreeViewer myTreeViewer = new TreeViewer(parent, SWT.BORDER | (allowMultiple ? SWT.MULTI : SWT.SINGLE));
		myTreeViewer.setContentProvider(new ContentProviderImplementation(myTreeViewer));
		myTreeViewer.setLabelProvider(new LabelProviderImplementation());
		myTreeViewer.setSorter(new NameSorter());
		myTreeViewer.setInput(fInput);
		doubleClickAction = new DoubleClickAction(myTreeViewer);
		
		myTreeViewer.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				doubleClickAction.run();
			}
		});
		myTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
			@Override
			public void selectionChanged(SelectionChangedEvent event) {
				setResult(((IStructuredSelection)event.getSelection()).toList());
				updateOKStatus();
			}
		});
		myTreeViewer.expandToLevel(((RootNode)fInput).areStoragesVisible() ? 3 : 2 );

		return myTreeViewer;
	}

	public TreeViewer getTreeViewer() {
		return treeViewer;
	}

	/**
	 * Returns a toolbar manager that can be used to add additional items to the
	 * toolbar above the tree viewer.
	 */
	public ToolBarManager getUpperToolBarManager() {
		return upperToolBarManager;
	}

	/**
	 * @see org.eclipse.jface.window.Window#handleShellCloseEvent()
	 */
	@Override
	protected void handleShellCloseEvent() {
		super.handleShellCloseEvent();

		// Handle the closing of the shell by selecting the close icon
		if (getReturnCode() == CANCEL) {
			setResult(null);
		}
	}

	private boolean isTreeEmpty() {
		return ((RootNode) treeViewer.getInput()).getChildren().size() == 0;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.window.Window#open()
	 */
	@Override
	public int open() {
		super.open();
		return getReturnCode();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.beans.PropertyChangeListener#propertyChange(java.beans.
	 * PropertyChangeEvent)
	 */
	public void propertyChange(PropertyChangeEvent arg0) {
		PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
			public void run() {
				updateOKStatus();
			}
		});

	}

	/**
	 * Sets the size of the tree in unit of characters.
	 * 
	 * @param width
	 *            the width of the tree.
	 * @param height
	 *            the height of the tree.
	 */
	public void setSize(int width, int height) {
		fWidth = width;
		fHeight = height;
	}

	/**
	 * Sets an optional validator to check if the selection is valid. The
	 * validator is invoked whenever the selection changes.
	 * 
	 * @param validator
	 *            the validator to validate the selection.
	 */
	public void setValidator(ISelectionStatusValidator validator) {
		fValidator = validator;
	}

	/**
	 * Validate the receiver and update the ok status.
	 * 
	 */
	protected void updateOKStatus() {
		if (!isTreeEmpty()) {
			if (fValidator != null) {
				fCurrStatus = fValidator.validate(getResult());
				updateStatus(fCurrStatus);
			} else {
				fCurrStatus = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
						IStatus.OK, "", //$NON-NLS-1$
						null);
			}
		} else {
			fCurrStatus = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
					IStatus.ERROR, fEmptyListMessage, null);
		}
		updateStatus(fCurrStatus);
	}

	public void setAllowMultiple(boolean allowMultiple) {
		this.allowMultiple = allowMultiple;
	}
}
